package com.treasury.glasses.controller;

import com.treasury.glasses.Main;
import com.treasury.glasses.controller.helper.Maskable;
import com.treasury.glasses.domain.ActivityLog;
import com.treasury.glasses.domain.fields.ActivityType;
import com.treasury.glasses.domain.ProgramUser;
import com.treasury.glasses.service.LoginService;
import java.io.IOException;
import java.net.URL;
import java.util.Date;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TabPane;
import javafx.scene.control.Tab;
import javafx.scene.control.TextField;

/**
 * Controller or BackEnd for the FrontEnd Login mask. Handles the mask
 * functionality and program states.
 * @author <a href="mailto:glasses@audienz.eu">Jean-Luc Burot</a>
 */

public class LoginController implements Initializable, Maskable {
    /**
     * Privately handled service class for this controller class.
     */
    private final LoginService service;
    
    /**
     * Link to the usernameLogin marked control on the mask.
     */
    @FXML
    private TextField usernameLogin;
    
    /**
     * Link to the passwordLogin marked control on the mask.
     */
    @FXML
    private PasswordField passwordLogin;
    
    /**
     * Link to the firstNameRegister marked control on the mask.
     */
    @FXML
    private TextField firstNameRegister;
    
    /**
     * Link to the lastNameRegister marked control on the mask.
     */
    @FXML
    private TextField lastNameRegister;
    
    /**
     * Link to the usernameRegister marked control on the mask.
     */
    @FXML
    private TextField usernameRegister;
    
    /**
     * Link to the passwordRegister marked control on the mask.
     */
    @FXML
    private PasswordField passwordRegister;
    
    /**
     * Link to the errorLabelLogin marked control on the mask.
     */
    @FXML
    private Label errorLabelLogin;
    
    /**
     * Link to the errorLabelRegister marked control on the mask.
     */
    @FXML
    private Label errorLabelRegister;
    
    /**
     * Link to the mainMenu marked control on the mask.
     */
    @FXML
    private TabPane mainMenu;
    
    /**
     * Link to the loginTab marked control on the mask.
     */
    @FXML
    private Tab loginTab;
    
    /**
     * Link to the registerTab marked control on the mask.
     */
    @FXML
    private Tab registerTab;
    
    /**
     * Link to the loginButton marked control on the mask.
     */
    @FXML
    private Button loginButton;
    
    /**
     * Takes the ActionEvent of the login Button on the mask.
     * @param event 
     */
    @FXML
    private void loginButtonAction(final ActionEvent event) {
        // Check which Tab has been selected.
        if (this.mainMenu.getSelectionModel().getSelectedItem().equals(this.loginTab)) {
            System.out.println("Login is selected.");
            
            try {
                loginProgramUser();
            } catch (IOException ex) {
                // TODO
                Logger.getLogger(LoginController.class.getName()).log(Level.SEVERE, null, ex);
            }
        } else if (this.mainMenu.getSelectionModel().getSelectedItem().equals(this.registerTab)) {
            System.out.println("Register is selected.");
            registerProgramUser();
        }
    }
    
    @FXML
    private void cancelButtonAction(final ActionEvent event) {
        closeProgram();
    }
    
    @FXML
    private void tabSelectionAction() {
        adjustButton();
    }
    
    /**
     * Controller for the Login mask.
     */
    public LoginController() {
        Main.setLoginController(this);
        this.service = new LoginService();
    }
    
    /**
     * Closes the program.
     */
    private void closeProgram() {
        // Shutdown the Program.
        System.exit(0);
    }
    
    /**
     * Adjusts the Login Button.
     */
    private void adjustButton() {
        // Set the Login Button text equal to the Tab text.
        if (this.loginTab != null && this.loginTab.isSelected()) {
            this.loginButton.setText(this.loginTab.getText());
        }
        if (this.registerTab != null && this.registerTab.isSelected()) {
            this.loginButton.setText(this.registerTab.getText());
        }
    }
    
    /**
     * Checks the existence of a ProgramUser and if found then logs him in.
     */
    private void loginProgramUser() throws IOException {
        ProgramUser programUser = this.service.checkUsernamePassword(this.usernameLogin.getText(),
                                                                     this.passwordLogin.getText());
        
        if (programUser != null) {
            // Set the accepted ProgramUser as logged in.
            Main.setLoggedInUser(programUser);
            
            // Create an ActivityLog.
            createActivityLog(Main.getLoggedInUser(), ActivityType.USER_LOGIN);
            
            // Close the Login mask and open the Scene.
            openScene();
        } else {
            this.errorLabelLogin.setText("Username or password incorrect.");
            // Empty password field.
            this.passwordLogin.setText("");
            // Set the focus to the password field.
            this.passwordLogin.requestFocus();
        }
    }
    
    /**
     * Logs out the ProgramUser.
     */
    public void logoutProgramUser() {
        Main.setLoggedInUser(null);
    }
    
    /**
     * Creates an ActivityLog.
     * @param programUser ProgramUser
     * @param activityType Type of activity
     */
    private void createActivityLog(ProgramUser programUser, ActivityType activityType) {
        System.out.println("Create ActivityLog " + activityType + " for ProgramUser " + programUser + ".");
        
        // Create an ActivityLog.
        ActivityLog activityLog = new ActivityLog();
        activityLog.setCreation(new Date());
        activityLog.setActivityType(activityType);
        activityLog.setProgramUser(programUser);
        System.out.println("activityType: " + activityType);
        System.out.println("activityLog: " + activityLog);

        // Persist the ActivityLog.
        this.service.createActivityLog(activityLog);
    }
    
    /**
     * Registers a ProgramUser assuming there has already been a redundancy
     * check.
     */
    private void registerProgramUser() {
        // Create user from fields.
        ProgramUser programUser = new ProgramUser();
        programUser.setFirstName(this.firstNameRegister.getText());
        programUser.setLastName(this.lastNameRegister.getText());
        programUser.setUsername(this.usernameRegister.getText());
        programUser.setPassword(this.passwordRegister.getText());
        
        System.out.println("Created ProgramUser: " + programUser);
        
        // Check ProgramUser exists.
        if (!this.service.checkProgramUserExists(programUser)) {
            // Empty error label.
            this.errorLabelRegister.setText("");
            
            // Persist ProgramUser.
            System.out.println("Persist ProgramUser");
            this.service.createProgramUser(programUser);
            
            // Create an ActivityLog.
            createActivityLog(Main.getLoggedInUser(), ActivityType.USER_REGISTRATION);
            
            // Set Login MenuPane as expanded.
            openLoginPane(this.usernameRegister.getText());
        } else {
            // ProgramUser exists already.
            this.errorLabelRegister.setText("Username already taken.");
            this.usernameRegister.requestFocus();
        }
    }
    
    /**
     * Opens and initializes the Login pane.
     * @param username Username
     */
    private void openLoginPane(final String username) {
        // Empty fields.
        emptyFields();
        
        // Initialize the login fields.
        this.usernameLogin.setText(username);
        
        // Set Login Pane as expanded.
        this.mainMenu.getSelectionModel().select(loginTab);
        
        // Set the focus to the password field.
        this.passwordLogin.requestFocus();
    }
    
    private void emptyFields() {
        // Emtpy login fields.
        this.usernameLogin.setText("");
        this.passwordLogin.setText("");
        
        // Empty registration fields.
        this.firstNameRegister.setText("");
        this.lastNameRegister.setText("");
        this.usernameRegister.setText("");
        this.passwordRegister.setText("");
        
        // Empty labels.
        this.errorLabelLogin.setText("");
        this.errorLabelRegister.setText("");
    }
    
    /**
     * Opens the Scene mask by replacing the Login mask.
     */
    private void openScene() throws IOException {
        Main.getSceneController().loadScene();
    }
    
    @Override
    public String getName() {
        return "Login";
    }
    
    @Override
    public final void initialize(final URL url, final ResourceBundle rb) {
        // Limit the text in TextFields.
        Main.addTextLimiter(usernameLogin, Main.TEXTFIELD_MAXLENGTH);
        Main.addTextLimiter(passwordLogin, Main.TEXTFIELD_MAXLENGTH);
        Main.addTextLimiter(firstNameRegister, Main.TEXTFIELD_MAXLENGTH);
        Main.addTextLimiter(lastNameRegister, Main.TEXTFIELD_MAXLENGTH);
        Main.addTextLimiter(usernameRegister, Main.TEXTFIELD_MAXLENGTH);
        Main.addTextLimiter(passwordRegister, Main.TEXTFIELD_MAXLENGTH);
        
        // Empty fields.
        emptyFields();
        
        // Request the focus for the username on the Login Pane.
        this.usernameLogin.requestFocus();
    }
}
